/* -+- file  ./kf2/Roster/Roster.cs -+- */
/*
 * kf2 jabber client
 * ~~~~~~~~~~~~~~~~~
 *
 * Copyright (C) 2006-2007 Przemysław Sitek
 * 				<przemyslaw.sitek at wp.pl> 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using Habazie.kf2.Net;

namespace Habazie.kf2.Roster
{
	///<summary>Roster manager</summary>
	public class Roster : IEnumerable
	{
		private LinkedList<Item> m_Items;
		private LinkedList<Group> m_Groups;
		private Account m_account;
		
		public event EventHandler RosterUpdated = null;
		public event RosterItemUpdatedEventHandler RosterItemUpdated = null;
		
		public Roster(Account a)
		{
			m_Items = new LinkedList<Item> ();
			m_Groups = new LinkedList<Group> ();
			
			Group m_Empty = new Group ("Empty");
			m_Groups.AddLast (m_Empty);
			
			m_account = a;
		}
		
		
		// FIXME: impelemnt IEnumerable
		public LinkedList<Item> Items {
			get { return m_Items; }
		}
		
		public ICollection<Group> Groups {
			get { return m_Groups; }
		}
		
		///<summary>Enumerate roster items</summary>
		public IEnumerator GetEnumerator ()
		{
			return m_Items.GetEnumerator ();
		}
		
		
		///<summary>Create a new item in a roster</summary>
		public Item CreateItem (jabber.JID jid)
		{
			Item i = new Item ();
			i.Account = m_account;
			i.JabberID = jid;
			
			Add (i);
			
			return i;
		}
		
		
		///<summary>Add item to a roster</summary>
		public void Add (Item item)
		{
			m_Items.AddLast (item);
		}
		
		
		///<summary>Get item for a JabberID</summary>
		public Item GetItem (jabber.JID jid)
		{
			foreach (Item i in m_Items)
				if (i.JabberID.Bare == jid.Bare)
					return i;
			return null;
		}
		
		public Group GetGroup (string name)
		{
			foreach (Group g in m_Groups)
				if (g.Name == name)
					return g;
			return null;
		}
		
		
		///<summary>Update roster contents</summary>
		public void Update (jabber.protocol.iq.Roster iq)
		{
			foreach (jabber.protocol.iq.Item item in iq.GetItems ()) {
				Item my_item = GetItem (item.JID);
				
				if (item.Subscription == jabber.protocol.iq.Subscription.remove) {
					// We want to remove this item
					if (my_item != null) {
						m_Items.Remove (my_item);
					}
				} else {
					// We want to add/remove this item				
					if (my_item != null) {
						my_item.Assign (item);
					} else {
						my_item = new Item (item);
						my_item.Account = m_account;
						Add (my_item);
					}
					
					/* Now, update groups */
				/*	foreach (Group g in m_Groups)	// remove
						g.Remove (my_item);
						
					foreach (string groupname in my_item.Groups) {
						Group g = GetGroup (groupname);
						if (g != null)
							g.Add (my_item);
					}
					if (my_item.Groups.Count == 0)
						m_Empty.Add (my_item);*/
					
				//	if (RosterUpdated != null)
				//		RosterUpdated (this, new RosterUpdatedEventArgs (my_item));
				}
			}
		//	Console.WriteLine ("Roster updated");
		//	Console.WriteLine ("{0}", this);
			
			if (RosterUpdated != null)
				RosterUpdated (this, new EventArgs ());
		}
		
		///<summary>Update roster contents</summary>
		public void Update (jabber.protocol.client.Presence p)
		{
			Item i = GetItem (p.From);
			if (i != null)
				i.Update (p);
			
			if (RosterItemUpdated != null)
				RosterItemUpdated (this, new RosterItemUpdatedEventArgs (i));
		}
		
		public override string ToString ()
		{
			System.Text.StringBuilder b = new System.Text.StringBuilder ();
			foreach (Item i in m_Items) {
				b.Append (" * ");
				b.Append (i.Name);
				b.Append (" <");
				b.Append (i.JabberID);
				b.Append (">\n");
			}
			return b.ToString ();
		}
		
		
		/// <summary>
		/// 	Inform roster that it has been disconnected
		/// </summary>
		public virtual void Disconnected ()
		{
			foreach (Item i in m_Items) {
				i.Disconnected ();
			}
			
			if (RosterUpdated != null)
				RosterUpdated (this, new EventArgs ());
		}
		
		/*
		 * Roster management
		 */
		
		
		/// <summary>
		/// 	Request adding an item to roster
		/// </summary>
		public void RequestAddItem (jabber.JID jid, string name, string group, bool subscribe)
		{
			jabber.protocol.iq.RosterIQ riq = new jabber.protocol.iq.RosterIQ (m_account.Document);
			riq.Type = jabber.protocol.client.IQType.set;
			jabber.protocol.iq.Roster query = riq.Query as jabber.protocol.iq.Roster;
			jabber.protocol.iq.Item item = query.AddItem ();
			item.JID = jid;
			if (name != null && name.Length > 0)
				item.Nickname = name;
			if (group != null && group.Length > 0)
				item.AddGroup (group);
			m_account.Write (riq);
			
			if (subscribe) {
				jabber.protocol.client.Presence p = new jabber.protocol.client.Presence (m_account.Document);
				p.To = jid;
				p.Type = jabber.protocol.client.PresenceType.subscribe;
				m_account.Write (p);
			}
			
			/* Should we wait for response? */
		}
		
		
		/// <summary>
		/// 	Request adding an item to roster
		/// </summary>
		public void RequestRemoveItem (jabber.JID jid, bool unsubscribe)
		{
			jabber.protocol.iq.RosterIQ riq = new jabber.protocol.iq.RosterIQ (m_account.Document);
			riq.Type = jabber.protocol.client.IQType.set;
			jabber.protocol.iq.Roster query = riq.Query as jabber.protocol.iq.Roster;
			jabber.protocol.iq.Item item = query.AddItem ();
			item.JID = jid;
			item.Subscription = jabber.protocol.iq.Subscription.remove;
			m_account.Write (riq);
			
			if (unsubscribe) {
				jabber.protocol.client.Presence p = new jabber.protocol.client.Presence (m_account.Document);
				p.To = jid;
				p.Type = jabber.protocol.client.PresenceType.unsubscribed;
				m_account.Write (p);
			}
			
			/* Should we wait for response? */
		}

		public void RequestSetGroup (Item roster_item, string group_name)
		{
			jabber.protocol.iq.RosterIQ riq = new jabber.protocol.iq.RosterIQ (m_account.Document);
			riq.Type = jabber.protocol.client.IQType.set;
			jabber.protocol.iq.Roster query = riq.Query as jabber.protocol.iq.Roster;
			jabber.protocol.iq.Item item = query.AddItem ();
			item.JID = roster_item.JabberID;
			string name = roster_item.Name;
			if (name != null && name.Length > 0)
				item.Nickname = name;
			if (group_name != null && group_name.Length > 0)
				item.AddGroup (group_name);
			m_account.Write (riq);
		}

		public void RequestRename (Item roster_item, string new_name)
		{
			jabber.protocol.iq.RosterIQ riq = new jabber.protocol.iq.RosterIQ (m_account.Document);
			riq.Type = jabber.protocol.client.IQType.set;
			jabber.protocol.iq.Roster query = riq.Query as jabber.protocol.iq.Roster;
			jabber.protocol.iq.Item item = query.AddItem ();
			item.JID = roster_item.JabberID;
			if (new_name != null)
				item.Nickname = new_name;
			foreach (string g in roster_item.Groups)
				item.AddGroup (g);
			m_account.Write (riq);
		}

	}

	public delegate void RosterItemUpdatedEventHandler (object sender, RosterItemUpdatedEventArgs args);
	
	public class RosterItemUpdatedEventArgs : System.EventArgs
	{
		private Item m_Item;
		
		public RosterItemUpdatedEventArgs (Item i)
		{
			m_Item = i;
		}
	
		// FIXME: a better name
		public Item UpdatedItem {
			get { return m_Item; }
		}
	}
}
